<template>
  <view
    :id="id"
    class="mxp-drag-button"
    :class="{ transition: adsorb && !data.moving }"
    :style="adsorbStyle"
    @touchstart="startDrag"
    @touchend="endDrag"
    @touchmove.stop.prevent="moveDrag"
    @click="onClick"
  >
    <slot></slot>
  </view>
</template>

<script lang="ts" setup>
  import type { $AllColorStr, $SizeUnitStr } from '@/mxp-ui/mxp-tool/types/interface';
  import { getOnlyID } from '@/mxp-ui/mxp-tool/utils/common';
  import { getXPNumber } from '@/mxp-ui/mxp-tool/utils/dom';
  import { publicStyeProps } from '@/mxp-ui/mxp-tool/utils/public-props';
  import { type CSSProperties, type PropType, computed, onMounted, reactive } from 'vue';
  import type { TouchEvent } from 'mxp-uni-types';
  import mxpGetSysInfoStore from '@/mxp-ui/mxp-tool/store/mxp-store-sys-info';
  defineOptions({
    name: 'mxp-drag-button',
  });

  const props = defineProps({
    /** 是否吸附边缘，关闭可以任意托动组件，开启拖动只会吸附在两边。 */
    adsorb: { type: Boolean, default: true },
    /** 开启吸附后，吸附到边缘的动画时间，单位ms */
    duration: { type: Number, default: 300 },
    /** 按钮的宽度 */
    width: { type: String as PropType<$SizeUnitStr>, default: '48px' },
    /** 按钮的高度 */
    height: { type: String as PropType<$SizeUnitStr>, default: '48px' },
    /** 默认的位置坐标 注意是相对于左下角的坐标  [x,y]  */
    location: { type: Array as unknown as PropType<[$SizeUnitStr, $SizeUnitStr]>, default: () => ['15px', '45px'] },
    /** 当吸附时的距离屏幕顶部的边距 */
    offsetTop: { type: String as PropType<$SizeUnitStr>, default: '15px' },
    /** 当吸附时的距离屏幕底部的边距 */
    offsetBottom: { type: String as PropType<$SizeUnitStr>, default: '15px' },
    /** 当吸附时的距离屏幕左边的边距 */
    offsetLeft: { type: String as PropType<$SizeUnitStr>, default: '15px' },
    /** 当吸附时的距离屏幕右边的边距 */
    offsetRight: { type: String as PropType<$SizeUnitStr>, default: '15px' },
    /** 背景 */
    background: { type: String as PropType<$AllColorStr>, default: 'rgba(0,0,0,0)' },
    /** 圆角 */
    borderradius: { type: String as PropType<$SizeUnitStr>, default: '99999px' },
    /** 层级 */
    zIndex: { type: Number, default: 888 },
    ...publicStyeProps,
  });
  defineSlots<{
    /** 主体内容 */
    default?: () => void;
  }>();
  const emits = defineEmits<{
    (e: 'click'): void;
  }>();
  const sys = mxpGetSysInfoStore().getSysInfo();
  const id = getOnlyID('drag-button');
  const data = reactive({
    windowBottom: sys.value.windowBottom,
    right: getXPNumber(props.location[0], 15),
    /** 距离底部的距离 */
    bottom: getXPNumber(props.location[1], 15) + sys.value.windowBottom,
    duration: 0,
    moving: false,
  });

  onMounted(() => {
    data.bottom = getXPNumber(props.location[1], 15) + sys.value.windowBottom;
    data.windowBottom = sys.value.windowBottom;
  });

  const newWidth = computed(() => getXPNumber(props.width, 48));
  const newHeight = computed(() => getXPNumber(props.height, 48));
  const newOffsetLeft = computed(() => getXPNumber(props.offsetLeft, 15));
  const newOffsetRight = computed(() => getXPNumber(props.offsetRight, 15));
  const newOffsetBottom = computed(() => getXPNumber(props.offsetBottom, 15));
  const newOffsetTop = computed(() => getXPNumber(props.offsetTop, 15));
  const adsorbStyle = computed(() => {
    return {
      zIndex: props.zIndex,
      backgroundColor: props.background,
      width: props.width,
      height: props.height,
      right: `${data.right}px`,
      bottom: `${data.bottom}px`,
      borderRadius: props.borderradius,
      transition: `all ${data.duration}ms ease-in-out`,
      ...props.cusStyle,
    } as CSSProperties;
  });

  const touchInfo = {
    pageX: 0,
    pageY: 0,
  };

  /**
   * 开始触控
   */
  function startDrag(event: TouchEvent) {
    const touch = event.touches[0]! || event.changedTouches[0]!;
    touchInfo.pageX = Math.round(touch.pageX || 0);
    touchInfo.pageY = Math.round(touch.pageY || 0);
    data.duration = 0;
  }

  /**
   * 触摸中
   */
  function moveDrag(event: TouchEvent) {
    data.moving = true;
    const touches = event.touches || event.changedTouches;
    if (touches.length === 1) {
      const { pageX, pageY } = touchInfo;
      const touch = touches[0]!;
      /** 左右移动的距离 */
      const x = touch.pageX! - pageX;
      /** 上下移动的距离 */
      const y = touch.pageY! - pageY;
      touchInfo.pageX = touch.pageX!;
      touchInfo.pageY = touch.pageY!;
      let right = data.right - x;
      if (right < newOffsetRight.value) {
        right = newOffsetRight.value;
      }
      if (sys.value.windowWidth - right - newWidth.value < newOffsetLeft.value) {
        right = sys.value.windowWidth - newWidth.value - newOffsetLeft.value;
      }

      let bottom = data.bottom - y;
      if (bottom < newOffsetBottom.value + sys.value.windowBottom) {
        bottom = newOffsetBottom.value + sys.value.windowBottom;
      }

      if (sys.value.windowHeight - newHeight.value + sys.value.windowBottom - bottom < newOffsetTop.value) {
        bottom = sys.value.windowHeight - newHeight.value + sys.value.windowBottom - newOffsetTop.value;
      }

      data.right = right;
      data.bottom = bottom;
    }
  }

  /**
   * 停止触摸
   */
  function endDrag() {
    data.moving = false;
    data.duration = props.duration;

    if (props.adsorb) {
      // 当前按钮位置中心点如果小于屏幕的一半就左边否则右边
      if (Math.abs(data.right + newWidth.value / 2) <= sys.value.windowWidth / 2) {
        data.right = newOffsetRight.value;
      } else {
        data.right = sys.value.windowWidth - newWidth.value - newOffsetRight.value;
      }
      if (sys.value.windowHeight - newHeight.value + sys.value.windowBottom - data.bottom < newOffsetTop.value) {
        data.bottom = sys.value.windowHeight - newHeight.value + sys.value.windowBottom - newOffsetTop.value;
      }

      if (data.bottom <= newOffsetBottom.value + sys.value.windowBottom) {
        data.bottom = newOffsetBottom.value + sys.value.windowBottom;
      }
    }
  }

  /** 点击事件 */
  function onClick() {
    emits('click');
  }
</script>

<style lang="scss" scoped>
  .mxp-drag-button {
    position: fixed;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;

    &.transition {
      transition: all 0.3s ease;
    }
  }
</style>
